<!doctype html>
<html>

<head>
    <meta charset="utf-8" />
    <title>2048</title>
    <link rel="stylesheet" href="../css/style.css" />
    <script src="jquery-3.1.1.min.js"></script>
</head>
<style type="text/css">
header {
    display: block;
    margin: 0 auto;
    text-align: center;
    width: 100%;
}

header>input {
    font-size: 20px;
    color: white;
    background-color: #605546;
    width: 100px;
    height: 40px;
    position: relative;
    top: -10px;
    border-radius: 10px;
    border: 0;
}

form {
    position: relative;
    left: 38px;
    top: -10px;
}

label {
    font-size: 25px;
}

label>input {
    border: 0;
    width: 100px;
    font-size: 25px;
}

table {
    margin: 0 auto;
    background-color: #bbada0;
    border-radius: 10px;
    overflow: hidden;
    position: relative;
}

tr {
    margin: 0;
}

td {
    width: 100px;
    border-style: none;
    height: 100px;
    float: left;
    background-color: #ccc0b3;
    border-radius: 10px;
    margin: 10px;
    text-align: center;
    font-size: 30px;
    font-style: bold;
    overflow: hidden;
}

p {
    position: absolute;
    margin: 0;
    height: 65px;
    width: 100px;
    padding-top: 35px;
    font-size: 20px;
    border-radius: 10px;
}
</style>

<body>
    <header>
        <h1>2048</h1>
        <input type="button" value="New game" id="start" />
        <form>
            <label>
                score:
                <input type="text" value="0" />
            </label>
        </form>
    </header>
    <section id="container">
        <table id="board">
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
        </table>
    </section>
</body>
<script type="text/javascript">
var board = new Array(), //每个格子的数字
    score = 0, //分数
    has_conflicted = new Array(), //解决连续消除的标记
    successString = 'Success',
    gameOverString = 'GameOver';

$("#start").click(function() {
    newGame();
})
$(newGame); //加载完之后开始游戏
function newGame() {
    //初始化数组
    for (var i = 0; i < 4; i++) {
        board[i] = new Array();
        has_conflicted[i] = new Array();
        for (var j = 0; j < 4; j++) {
            board[i][j] = 0;
            has_conflicted[i][j] = false;
        }
    }
    //通过newGame按钮开始游戏时初始化棋盘
    updateBoardView();
    score = 0;
    updateScore(score); //通过newGame按钮开始游戏时初始化分数
    //棋盘上随机出现两个数字
    generateOneNum();
    generateOneNum();
}

//随机一个位置和一个数字
//并将数字显示出来
function generateOneNum() {
    //如果棋盘已满返回
    if (noSpace()) {
        return false;
    }
    var randX, randY, time = 0;
    //最多随机50次
    //保证随机出来的位置上为空
    do {
        time++;
        randX = Math.floor(Math.random() * 4);
        randY = Math.floor(Math.random() * 4);
    } while (board[randX][randY] && (time < 50))
    //50次之后依然随机不到，循环查找空位
    if (time == 50) {
        for (var i = 0; i < 4; i++) {
            for (var j = 0; j < 4; j++) {
                if (!board[i][j]) {
                    randX = i;
                    randY = j;
                }
            }
        }
    }
    //随机出一个数字2或者4
    randNum = Math.random() > 0.1 ? 2 : 4;
    board[randX][randY] = randNum; //设定棋盘上的数字
    showNumAnimate(randX, randY, randNum); //动态显示数字
    return true;
}
//动态显示数字
function showNumAnimate(i, j, randNum) {
    //为目标单元格添加p元素
    var tem = $("tr:eq(" + i + ")").children("td:eq(" + j + ")")
        .append("<p>" + randNum + "</p>")
        .children("p");
    //动态显示数字
    tem.css("background-color", getNumBackcolor(randNum));
    tem.css("color", getNumColor(randNum));
    tem.fadeIn("normal");
}
//根据不同数字显示不同北京颜色
function getNumBackcolor(num) {
    switch (num) {
        case 2:
            return "#eee4da";
            break;
        case 4:
            return "#eee0c8";
            break;
        case 8:
            return '#f2b179';
            break;
        case 16:
            return '#f59563';
            break;
        case 32:
            return '#f67c5f';
            break;
        case 64:
            return '#f65e3b';
            break;
        case 128:
            return '#edcf72';
            break;
        case 256:
            return '#edcc61';
            break;
        case 512:
            return '#9c0';
            break;
        case 1024:
            return '#33b5e5';
            break;
        case 2048:
            return '#09c';
            break;
        case 4096:
            return '#a6c';
            break;
        case 8192:
            return '#93c';
            break;
    }
    return "black";
}
//根据不同数字显示不同字体颜色
function getNumColor(num) {
    if (num > 4) {
        return "snow";
    } else {
        return "#776e65";
    }
}
//重绘棋盘
function updateBoardView() {
    $("td").empty();
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            if (board[i][j]) {
                var tem = $("tr:eq(" + i + ")").children("td:eq(" + j + ")")
                    .append("<p>" + board[i][j] + "</p>")
                    .children("p");
                tem.css("background-color", getNumBackcolor(board[i][j]));
                tem.css("color", getNumColor(board[i][j]));
                tem.show();
            }
            has_conflicted[i][j] = false;
        }
    }
}

function noSpace() {
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            if (!board[i][j]) {
                return false;
            }
        }
    }
    return true;
}

function updateScore() {
    $("label>input").attr("value", "" + score);
}

var flag = true; //标识是否出现2048，仅提示一次游戏成功
//绑定键盘事件
$(document).keydown(function(event) {
    if ($("label>input").text() == successString) {
        newGame();
        return;
    }
    //阻止默认事件，移动之后生成一个新的数字，并判断游戏是否结束
    event.preventDefault();
    if (move(event.keyCode)) {
        setTimeout("generateOneNum()", 210);
        setTimeout('isGameover()', 300);
    }
});
//根据方向进行移动
function move(direction) {
    //判断能否在当前方向上移动，若不能移动返回false
    if (!canMove(direction)) {
        return false;
    }
    //根据方向选择遍历的起点
    //查找非空元素时左/下从第二行/列开始查找，右/上从第三行/列进行查找(a来标识)
    //判断移动位置时左/下从第一行/列开始判断，右/上从第四行/列进行判断（k来标识）
    //b用来标识行/列结束位置
    //c用来标识步进
    var a = 1,
        b = 4,
        c = 1,
        d = 0;
    if (direction == 39 || direction == 40) {
        a = 2;
        b = -1;
        c = -1;
        d = 3;
    }
    for (var i = 0; i < 4; i++) {
        for (var j = a; j != b; j += c) { //循环查找非空元素
            switch (direction) {
                case 37:
                case 39:
                    if (board[i][j]) {
                        for (var k = d; direction == 37 ? (k < j) : (k > j); k += c) {
                            //当前元素为空且待移动元素和当前元素之间没有非空元素
                            if (!board[i][k] && (direction == 37 ? no_block_horizontal(i, k, j) : no_block_horizontal(i, j, k))) {
                                show_move_animation(i, j, i, k); //移动
                                //更新board数组
                                board[i][k] = board[i][j];
                                board[i][j] = 0;
                                break;
                            } else if (board[i][k] == board[i][j] &&
                                (direction == 37 ? no_block_horizontal(i, k, j) : no_block_horizontal(i, j, k)) &&
                                !has_conflicted[i][k]) { //当前非空元素和移动元素相等，之间没有非空元素，之前没有合并过
                                show_move_animation(i, j, i, k);
                                board[i][k] += board[i][j];
                                board[i][j] = 0;
                                //更新分数，并置合并标志
                                score += board[i][k];
                                updateScore(score);
                                has_conflicted[i][k] = true;
                                break;
                            }
                        }
                    }
                    break;
                case 38:
                case 40:
                    if (board[j][i]) {
                        for (var k = d; direction == 38 ? (k < j) : (k > j); k += c) {
                            if (!board[k][i] && (direction == 38 ? no_block_vertical(i, k, j) : no_block_vertical(i, j, k))) {
                                show_move_animation(j, i, k, i);
                                board[k][i] = board[j][i];
                                board[j][i] = 0;
                                break;
                            } else if (board[k][i] == board[j][i] &&
                                (direction == 38 ? no_block_vertical(i, k, j) : no_block_vertical(i, j, k)) &&
                                !has_conflicted[k][i]) {
                                show_move_animation(j, i, k, i);
                                board[k][i] += board[j][i];
                                board[j][i] = 0;
                                score += board[k][i];
                                updateScore(score);
                                has_conflicted[k][i] = true;
                                break;
                            }
                        }
                    }
                    break;
            }
        }
    }
    setTimeout("updateBoardView()", 200); //根据board数组更新棋盘
    return true;
}
//判断水平方向上两个元素之间是否为空
function no_block_horizontal(row, col1, col2) {
    for (var i = col1 + 1; i < col2; i++) { //从当前列的下一列到第二个元素的前一列
        if (board[row][i]) { //如果元素不为空返回false
            return false;
        }
    }
    return true;
}
//判断垂直方向上两个元素是否为空
function no_block_vertical(col, row1, row2) {
    for (var i = row1 + 1; i < row2; i++) {
        if (board[i][col]) {
            return false;
        }
    }
    return true;
}
//移动元素
function show_move_animation(fromX, fromY, toX, toY) {
    var tem = $("tr:eq(" + fromX + ")").children("td:eq(" + fromY + ")").children("p");
    tem.animate({
        top: (toX * (100 + 23.6) + 13) + "px",
        left: (toY * (100 + 23) + 13) + "px"
    }, 100);
}
//判断当前方向上是否能移动
function canMove(direction) {
    //类似move函数中的设定
    var a = 1,
        b = 4,
        c = 1;
    if (direction == 39 || direction == 40) {
        a = 2;
        b = -1;
        c = -1;
    }
    for (var i = 0; i < 4; i++) {
        for (var j = a; j != b; j += c) {
            switch (direction) {
                case 37:
                case 39:
                    if (board[i][j]) {
                        if (board[i][j - c] == 0 || board[i][j] == board[i][j - c]) {
                            return true;
                        }
                    }
                    break;
                case 38:
                case 40:
                    if (board[j][i]) {
                        if (board[j - c][i] == 0 || board[j][i] == board[j - c][i]) {
                            return true;
                        }
                    }
                    break;
            }
        }
    }
    return false;
}
//判断游戏是否结束
function isGameover() {
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            if (board[i][j] == 2048) {
                if (flag) {
                    alert(successString);
                    flag = false;
                    return;
                }
            }
        }
    }
    if (noSpace() && noMove()) {
        gameOver();
    }
}
//游戏结束提示信息
function gameOver() {
    alert(gameOverString + "\n你的分数是:" + $("label>input").val());
    newGame();
}
//判断当前是否不能移动
function noMove() {
    if (canMove(37) || canMove(38) || canMove(39) || canMove(40)) {
        return false;
    }
    return true;
}
</script>

</html>
